﻿namespace OverStore.Configuration.Storage
{
    using System;
    using System.Data;
    using OverStore.Configuration.Validation;
    using OverStore.Exceptions.Configuration;
    using OverStore.Runtime.OverStoreContext;
    using OverStore.Runtime.Storage;
using OverStore.Sql;
using System.Collections.Generic;

    /// <summary>
    /// Configuration of component allows to persist data in RDBMS which has ADO.NET compatible provider.
    /// </summary>
    public class AdoNetStorageConfiguration<TConnection> : IComponentConfiguration<IDataStorage>, ISupportValidation
        where TConnection : IDbConnection
    {
        private static Dictionary<string, SqlFormatting> _PredefinedFormatting = new Dictionary<string, SqlFormatting>
        {
            { "SqlConnection", new SqlServerFormatting() },
            { "SqlCeConnection", new SqlServerCeFormatting() }
        };

        /// <summary>
        /// Initializes a new instance of the <see cref="AdoNetStorageConfiguration&lt;TConnection&gt;"/> class.
        /// </summary>
        public AdoNetStorageConfiguration()
        {
            CommitWithOpenedReadersMode = ProcessOpenedReadersMode.ForciblyCloseReaders;
        }

        /// <summary>
        /// Gets or sets the connection string.
        /// </summary>
        /// <result>The connection string.</result>
        public string ConnectionString { get; set; }

        /// <summary>
        /// Gets or sets the SQL formatting.
        /// </summary>
        public SqlFormatting SqlFormatting { get; set; }

        /// <summary>
        /// Gets or sets value defines storage behavior on transaction ending while opened readers exist.
        /// </summary>
        public ProcessOpenedReadersMode CommitWithOpenedReadersMode { get; set; }

        /// <summary>
        /// Creates the <see cref="IDataStorage"/> with specified configuration.
        /// </summary>
        /// <param name="context">The context.</param>
        /// <returns>
        /// The <see cref="IDataStorage"/> instance with specified configuration.
        /// </returns>
        public IDataStorage CreateComponent(IOverStoreContext context)
        {
            if (Object.ReferenceEquals(context, null))
                throw new ArgumentNullException("context");
            if (String.IsNullOrEmpty(ConnectionString))
                throw this.ConfigurationException("Connections String is missing.");
            var formatting = SqlFormatting;
            if (formatting == null)
            {
                var name = typeof(TConnection).Name;
                _PredefinedFormatting.TryGetValue(name, out formatting);
            }

            return new AdoNetStorage<TConnection>(ConnectionString, CommitWithOpenedReadersMode, context.Logger, formatting, context.ScopeManager);
        }

        /// <summary>
        /// Perform component validation. Method should validate only
        /// component configuration itself, not sub components.
        /// If configuration is valid, null result should returns.
        /// </summary>
        /// <returns>
        ///     <see cref="T:OverStore.Configuration.Validation.ValidationResults"/> describes errors found in component configuration.
        /// </returns>
        public ValidationResult Validate()
        {
            return new ValidationResult(this)
                .AddError(String.IsNullOrEmpty(ConnectionString), "Connection String is missing.");
        }
    }
}
